package jmind.core.image.code;

import jmind.base.util.FileUtil;
import jmind.base.util.RandUtil;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Iterator;

/**
 * 滑动图片验证码
 * 根据模板生成图片验证码
 * @author weibo
 * @date 2019/07/17
 * https://blog.csdn.net/ONROAD0612/article/details/81197158
 * https://github.com/liutao910612/TOOLS_other.git
 */
public class VerifyImageUtil {

    public static void main(String[] args) throws Exception {
        File  templateFile=new File("/Users/weibo/workspace/learn/TOOLS_other/util/validate/src/main/resources/static/templates/3.png");
        File  orifile=new File("/Users/weibo/workspace/learn/TOOLS_other/util/validate/src/main/resources/static/targets/1.jpg");

        ImageCode imageCode = cutByTemplate(orifile,templateFile);
        ImageIO.write(imageCode.getTemplateImage(),"png",new File("/Users/weibo/Downloads/vcode/t1.png"));
        ImageIO.write(imageCode.getTargetImage(),"png",new File("/Users/weibo/Downloads/vcode/s1.png"));
    }

    /**
     * 根据模板切图
     * @param orifile      原图
     * @param templateFile 模板
     * @return
     * @throws Exception
     */
    public static ImageCode cutByTemplate(File orifile,File templateFile) throws Exception {
        // 源文件流
        BufferedImage oriImage = ImageIO.read(orifile);
        InputStream oriis = new FileInputStream(orifile);

        // 模板图
        BufferedImage imageTemplate = ImageIO.read(templateFile);
        int width = imageTemplate.getWidth();
        int height = imageTemplate.getHeight();
        int widthDifference = oriImage.getWidth() - width;
        int heightDifference = oriImage.getHeight() - height;
        int x= widthDifference>0 ? RandUtil.nextInt(widthDifference)+5: 5 ;
        int y=heightDifference>0 ? RandUtil.nextInt(heightDifference)+5: 5;

        // 最终图像
        BufferedImage newImage = new BufferedImage(width, height, imageTemplate.getType());
        Graphics2D graphics = newImage.createGraphics();
        graphics.setBackground(Color.white);

        int bold = 5;
        // 获取感兴趣的目标区域
        BufferedImage targetImageNoDeal = getTargetArea(x, y, width, height, oriis, FileUtil.getExtensionName(orifile.getName()));


        // 根据模板图片抠图
        newImage = DealCutPictureByTemplate(targetImageNoDeal, imageTemplate, newImage);

        // 设置“抗锯齿”的属性
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        graphics.drawImage(newImage, 0, 0, null);
        graphics.dispose();
        //新建流。
        //ByteArrayOutputStream os = new ByteArrayOutputStream();
        ////利用ImageIO类提供的write方法，将bi以png图片的数据模式写入流。
        //ImageIO.write(newImage, "png", os);
        //byte[] newImages = os.toByteArray();
        //pictureMap.put("newImage", newImages);

        // 源图生成遮罩

        BufferedImage oriCopyImages = DealOriPictureByTemplate(oriImage, imageTemplate, x, y);
        ImageCode imageCode=new ImageCode();
        imageCode.setX(x);
        imageCode.setY(y);
        imageCode.setTemplateImage(newImage);
        imageCode.setTargetImage(oriCopyImages);
        return imageCode;
    }

    /**
     * 抠图后原图生成
     *
     * @param oriImage
     * @param templateImage
     * @param x
     * @param y
     * @return
     * @throws Exception
     */
    private static BufferedImage DealOriPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage, int x,
                                                   int y) throws Exception {
        // 源文件备份图像矩阵 支持alpha通道的rgb图像
        BufferedImage ori_copy_image = new BufferedImage(oriImage.getWidth(), oriImage.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);

        //copy 源图做不透明处理
        for (int i = 0; i < oriImageData.length; i++) {
            for (int j = 0; j < oriImageData[0].length; j++) {
                int rgb = oriImage.getRGB(i, j);
                int r = (0xff & rgb);
                int g = (0xff & (rgb >> 8));
                int b = (0xff & (rgb >> 16));
                //无透明处理
                rgb = r + (g << 8) + (b << 16) + (255 << 24);
                ori_copy_image.setRGB(i, j, rgb);
            }
        }

        for (int i = 0; i < templateImageData.length; i++) {
            for (int j = 0; j < templateImageData[0].length - 5; j++) {
                int rgb = templateImage.getRGB(i, j);
                //对源文件备份图像(x+i,y+j)坐标点进行透明处理
                if (rgb != 16777215 && rgb <= 0) {
                    int rgb_ori = ori_copy_image.getRGB(x + i, y + j);
                    int r = (0xff & rgb_ori);
                    int g = (0xff & (rgb_ori >> 8));
                    int b = (0xff & (rgb_ori >> 16));
                    rgb_ori = r + (g << 8) + (b << 16) + (150 << 24);
                    ori_copy_image.setRGB(x + i, y + j, rgb_ori);
                } else {
                    //do nothing
                }
            }
        }
        //ByteArrayOutputStream os = new ByteArrayOutputStream();//新建流。
        //ImageIO.write(ori_copy_image, "png", os);//利用ImageIO类提供的write方法，将bi以png图片的数据模式写入流。
        //byte b[] = os.toByteArray();//从流中获取数据数组。
        return ori_copy_image;
    }


    /**
     * 根据模板图片抠图
     *
     * @param oriImage
     * @param templateImage
     * @return
     */

    private static BufferedImage DealCutPictureByTemplate(BufferedImage oriImage, BufferedImage templateImage,
                                                          BufferedImage targetImage) throws Exception {
        // 源文件图像矩阵
        int[][] oriImageData = getData(oriImage);
        // 模板图像矩阵
        int[][] templateImageData = getData(templateImage);
        // 模板图像宽度

        for (int i = 0; i < templateImageData.length; i++) {
            // 模板图片高度
            for (int j = 0; j < templateImageData[0].length; j++) {
                // 如果模板图像当前像素点不是白色 copy源文件信息到目标图片中
                int rgb = templateImageData[i][j];
                if (rgb != 16777215 && rgb <= 0) {
                    targetImage.setRGB(i, j, oriImageData[i][j]);
                }
            }
        }
        return targetImage;
    }


    /**
     * 获取目标区域
     *
     * @param x            随机切图坐标x轴位置
     * @param y            随机切图坐标y轴位置
     * @param targetWidth  切图后目标宽度
     * @param targetHeight 切图后目标高度
     * @param ois          源文件输入流
     * @return
     * @throws Exception
     */
    private static BufferedImage getTargetArea(int x, int y, int targetWidth, int targetHeight, InputStream ois,
                                               String filetype) throws Exception {
        Iterator<ImageReader> imageReaderList = ImageIO.getImageReadersByFormatName(filetype);
        ImageReader imageReader = imageReaderList.next();
        // 获取图片流
        ImageInputStream iis = ImageIO.createImageInputStream(ois);
        // 输入源中的图像将只按顺序读取
        imageReader.setInput(iis, true);
        ImageReadParam param = imageReader.getDefaultReadParam();
        Rectangle rec = new Rectangle(x, y, targetWidth, targetHeight);
        param.setSourceRegion(rec);
        BufferedImage targetImage = imageReader.read(0, param);
        return targetImage;
    }

    /**
     * 生成图像矩阵
     *
     * @param
     * @return
     * @throws Exception
     */
    private static int[][] getData(BufferedImage bimg) throws Exception {
        int[][] data = new int[bimg.getWidth()][bimg.getHeight()];
        for (int i = 0; i < bimg.getWidth(); i++) {
            for (int j = 0; j < bimg.getHeight(); j++) {
                data[i][j] = bimg.getRGB(i, j);
            }
        }
        return data;
    }

}
